-
-
Notifications
You must be signed in to change notification settings - Fork 598
feat(iOS, Tabs): add bottomAccessory support #3288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
t0maboro
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't finished yet, flushing the 1st part
common/cpp/react/renderer/components/rnscreens/RNSBottomTabsAccessoryComponentDescriptor.h
Show resolved
Hide resolved
common/cpp/react/renderer/components/rnscreens/RNSBottomTabsAccessoryShadowNode.cpp
Outdated
Show resolved
Hide resolved
common/cpp/react/renderer/components/rnscreens/RNSBottomTabsAccessoryShadowNode.cpp
Outdated
Show resolved
Hide resolved
common/cpp/react/renderer/components/rnscreens/RNSBottomTabsAccessoryState.h
Outdated
Show resolved
Hide resolved
ios/bottom-tabs/accessory/RNSBottomTabsAccessoryComponentView.mm
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really good job. The PR is in really good shape & code is of high quality - kudos.
I've got few remarks, refactor requests & questions. Please answer them.
common/cpp/react/renderer/components/rnscreens/RNSBottomTabsAccessoryComponentDescriptor.h
Show resolved
Hide resolved
kkafar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks nice. I've added few more suggestions & answered some questions.
ios/bottom-tabs/accessory/RNSBottomTabsAccessoryContentComponentView.h
Outdated
Show resolved
Hide resolved
| #if BOTTOM_ACCESSORY_AVAILABLE | ||
|
|
||
| #import <React/RCTAssert.h> | ||
| #include <cxxreact/ReactNativeVersion.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why all of a sudden include here, instead of import? Also the check for cplusplus is missing? Or am I missing something? Maybe its included somewhere transitively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixing include -> import here: b521658.
Also the check for cplusplus is missing?
I don't think that they are necessary in .mm file, right?
| - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask | ||
| { | ||
| [super finalizeUpdates:updateMask]; | ||
| [_accessoryView.helper handleContentViewVisibilityForEnvironmentIfNeeded]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RCTViewComponentView changes view opacity via view.layer.opacity, e.g. in updateProps and invalidateLayer -> this overrides our setting. In order to bring back correct values, I update the visibility here.
Please document this in code. Because this is something you can not come up with by just reading the code.
Also document / create a ticket for the bug with devtools you described. Let's not let it go under radar.
That said, I can see that the RCTViewComponentView diffs the opacity before mutating it & it diffs it with prop values, not the value applied on the layer.
Does this problem occur only in first render then? Or how does it work otherwise?
Description
Adds support for
botttomAccessoryin Bottom Tabs starting from iOS 26.Synchronization with ShadowTree
When bottom accessory transitions between
regularandinlineenvironments (when tab bar is minimized), we need to update the position and size of the bottom accessory. Our approach is different for RN < 0.82 and RN >= 0.82.Possible approaches
We considered following approaches:
double-renderingapproach (explained further in PR description) wouldn't really improve the situationCoreAnimationsframework that animates views natively (details explained further in PR description)immediatemode for state update introduced in RN 0.82 but "synchronous" event dispatch in RN 0.82 isn't exactlysynchronous(update is performed on next loop beat, this is not enough for CA)Mounting/unmounting views during transitionsection)double-renderingregularenvironment, second forinlineenvironmentFor now, we decided to use:
Those solutions are described in more detail below:
Legacy architecture & New architecture prior to
[email protected]In versions prior to RN 0.82, we need use
DisplayLinkand presentation layer frames to get intermediate frames during the transition. This approach however has a major drawback - we are always at least one frame behind the current state as we're observing what is currently presented. When the difference in size/origin between frames is significant, you can see the content "jumping". In the case of bottom accessory, this is especially visible when using non-translucent background and transitioning frominlinetoregularenvironment (pay attention to the right edge of the accessory).Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.08.31.39.mov
Introduction of synchronous state updates in RN 0.82 (more details below) does not improve the situation when using this approach as we are still going to be at least one frame behind the animation.
[email protected]and higherThanks to introduction of synchronous state updates in RN 0.82, we can rely fully on native mechanisms for handling the transition. Bottom accessory only receives the final frame of the transition and thanks to synchronous state updates, we can immediately recalculate the layout in the Shadow Tree and update the Host Tree. This allows Core Animation framework to make the transition smooth. Details of how we think this works are available here.
Unfortunately, when using
react-native, the situation is a little bit more complicated.Text
Text component behaves differently to the native platform. During the transition, it immediately adapts to the final frame size and then it is stretched. In bare UIKit app, the text adapt to new frame size at the end of the transition.
react-nativeUIKitSimulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.10.16.33.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.10.22.22.mov
This requires more investigation and potentially changes in
react-native.Borders
CoreAnimationdoes not support non-uniform borders soreact-nativehandles them in a custom way that does not seem to be compatible with the transition mechanism.Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.10.28.39.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.10.32.17.mov
This requires more investigation and potentially changes in
react-native.Images
Similar problem (in a way it looks, not the exact mechanism of the bug) happens when using images e.g. with
width: 100%.Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.11.08.09.mov
This requires more investigation and potentially changes in
react-native.Mounting/unmounting views during transition
While state updates are performed synchronously, any changes to React Element Tree in reaction to environment change are handled asynchronously. We think that this is why the transition handled by
CoreAnimationbreaks when trying to mount/unmount components on environment change.Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-10-13.at.08.16.12.mov
Here, we try to remove the note icon. Unfortunately, the rest of the layout does not adapt. You can also observe the text stretching as mentioned 2 sections above.
In order to mitigate this issue, we use
double-renderingapproach, which has been described inPossible approachessection.Simulator.Screen.Recording.-.iPhone.17.Pro.-.2025-11-10.at.13.50.25.mov
Changes
BottomTabsAccessoryJS component and use it inBottomTabs,BottomTabsAccessoryComponentView,BottomTabsAccessoryEventEmitter,BottomTabsAccessoryComponentViewManager,BottomAccessoryHelperto handle size and environment changes,BottomTabsAccessoryShadowStateProxyto synchronize state between Host and ShadowTree,BottomTabsHostto accept 2 types of children (ScreenandAccessory),Test code and steps to reproduce
Run
Test3288.Checklist